using System;
using System.Collections.Generic;
using System.Text;
using Csla;
using Templates;
using NBearLite;
using Active.Core.DB;
using Csla.Data;

namespace Active.Core
{
    [Serializable()]
    public class Customer : BusinessBase<Customer>
    {
        #region Business Methods
        // TODO: add your own fields, properties and methods
        private int _id;
        public int Id
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _id;
            }
        }

        string _name = string.Empty;
        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _name;
            }
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            set
            {
                CanWriteProperty(true);
                if (value == null) value = string.Empty;
                if (!(_name == value))
                {
                    _name = value;
                    PropertyHasChanged();
                }
            }
        }

        string _address = string.Empty;
        /// <summary>
        /// Gets or sets the address.
        /// </summary>
        /// <value>The address.</value>
        public string Address
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _address;
            }
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            set
            {
                CanWriteProperty(true);
                if (value == null) value = string.Empty;
                if (!_address.Equals(value))
                {
                    _address = value;
                    PropertyHasChanged();
                }
            }
        }

        string _fax = string.Empty;
        /// <summary>
        /// Gets or sets the fax.
        /// </summary>
        /// <value>The fax.</value>
        public string Fax
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _fax;
            }
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            set
            {
                CanWriteProperty(true);
                if (value == null) value = string.Empty;
                if (!_fax.Equals(value))
                {
                    _fax = value;
                    PropertyHasChanged();
                }
            }
        }

        string _tel = string.Empty;
        /// <summary>
        /// Gets or sets the tel.
        /// </summary>
        /// <value>The tel.</value>
        public string Tel
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _tel;
            }
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            set
            {
                CanWriteProperty(true);
                if (value == null) value = string.Empty;
                if (!_tel.Equals(value))
                {
                    _tel = value;
                    PropertyHasChanged();
                }
            }
        }

        int _dealTimes;
        /// <summary>
        /// Gets or sets the deal times.
        /// </summary>
        /// <value>The deal times.</value>
        public int DealTimes
        {
            [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
            get
            {
                CanReadProperty(true);
                return _dealTimes;
            }
        }

        private Products _products = Products.NewProducts();
        /// <summary>
        /// Gets the products.
        /// </summary>
        /// <value>The products.</value>
        public Products Products
        {
            get
            {
                return _products;
            }
        }

        Contacts _contactList;
        /// <summary>
        /// Gets the contact list.
        /// </summary>
        /// <value>The contact list.</value>
        public Contacts ContactList
        {
            get
            {
                if (_contactList == null && _id > 0)
                {
                    _contactList = Contacts.GetCustomerContacts(_id);
                }
                return _contactList;
            }
        }

        /// <summary>
        /// Override this method to return a unique identifying
        /// value for this object.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// If you can not provide a unique identifying value, it
        /// is best if you can generate such a unique value (even
        /// temporarily). If you can not do that, then return
        /// <see langword="Nothing"/> and then manually override the
        /// <see cref="Equals"/>, <see cref="GetHashCode"/> and
        /// <see cref="ToString"/> methods in your business object.
        /// </remarks>
        protected override object GetIdValue()
        {
            return _id;
        }

        public override bool IsDirty
        {
            get
            {
                return base.IsDirty || _products.IsDirty;
            }
        }

        public override bool IsValid
        {
            get
            {
                return base.IsValid && _products.IsValid;
            }
        }

        #endregion

        #region Validation Rules

        /// <summary>
        /// Override this method in your business class to
        /// be notified when you need to set up shared
        /// business rules.
        /// </summary>
        /// <remarks>
        /// This method is automatically called by CSLA .NET
        /// when your object should associate per-type
        /// validation rules with its properties.
        /// </remarks>
        protected override void AddBusinessRules()
        {
            // TODO: add validation rules
            //ValidationRules.AddRule(null, "");
            ValidationRules.AddRule(Csla.Validation.CommonRules.StringRequired, "Name");
        }

        #endregion

        #region Authorization Rules

        /// <summary>
        /// Override this method to add per-type
        /// authorization rules for your type's properties.
        /// </summary>
        /// <remarks>
        /// AddAuthorizationRules is automatically called by CSLA .NET
        /// when your object should associate per-type authorization roles
        /// with its properties.
        /// </remarks>
        protected override void AddAuthorizationRules()
        {
            // TODO: add authorization rules
            //AuthorizationRules.AllowWrite("", "");
        }

        /// <summary>
        /// Determines whether this instance [can add object].
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance [can add object]; otherwise, <c>false</c>.
        /// </returns>
        public static bool CanAddObject()
        {
            return Utils.IsCustomerEmployee
                || Utils.IsCustomerManager;
        }

        /// <summary>
        /// Determines whether this instance [can get object].
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance [can get object]; otherwise, <c>false</c>.
        /// </returns>
        public static bool CanGetObject()
        {
            return true;
        }

        /// <summary>
        /// Determines whether this instance [can edit object].
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance [can edit object]; otherwise, <c>false</c>.
        /// </returns>
        public static bool CanEditObject()
        {
            return Utils.IsCustomerEmployee
                || Utils.IsCustomerManager;
        }

        /// <summary>
        /// Determines whether this instance [can delete object].
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this instance [can delete object]; otherwise, <c>false</c>.
        /// </returns>
        public static bool CanDeleteObject()
        {
            return Utils.IsCustomerManager;
        }

        #endregion

        #region Factory Methods

        /// <summary>
        /// News the Customer.
        /// </summary>
        /// <returns></returns>
        public static Customer NewCustomer()
        {
            if (!CanAddObject())
                throw new System.Security.SecurityException(
                  "User not authorized to add an Customer");
            return new Customer();
        }

        /// <summary>
        /// Gets the Customer.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        public static Customer GetCustomer(int id)
        {
            if (!CanGetObject())
                throw new System.Security.SecurityException(
                  "User not authorized to view an Customer");
            return DataPortal.Fetch<Customer>(new Criteria(id));
        }

        public static Customer GetCustomer(string name)
        {
            if (!CanGetObject())
                throw new System.Security.SecurityException(
                  "User not authorized to view an Customer");
            return DataPortal.Fetch<Customer>(new Criteria(name));
        }

        /// <summary>
        /// Checks the customer name is used.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public static bool CheckNameIsUsed(string name)
        {
            CheckNameCommand cmd = new CheckNameCommand(name);
            Csla.DataPortal.Execute<CheckNameCommand>(cmd);
            return cmd.Result;
        }

        /// <summary>
        /// Deletes the Customer.
        /// </summary>
        /// <param name="id">The id.</param>
        public static void DeleteCustomer(int id)
        {
            if (!CanDeleteObject())
                throw new System.Security.SecurityException(
                  "User not authorized to remove an Customer");
            DataPortal.Delete(new Criteria(id));
        }
        
        public override Customer Save()
        {
            if (IsDeleted && !CanDeleteObject())
                throw new System.Security.SecurityException(
                  "User not authorized to remove an Customer");
            else if (IsNew && !CanAddObject())
                throw new System.Security.SecurityException(
                  "User not authorized to add an Customer");
            else if (!CanEditObject())
                throw new System.Security.SecurityException(
                  "User not authorized to update an Customer");
            return base.Save();
        }

        private Customer()
        { /* Require use of factory methods */ }

        #endregion

        #region Data Access

        [Serializable()]
        private class Criteria
        {
            private int _id;
            public int Id
            {
                get { return _id; }
            }
            private string _name;
            public string Name
            {
                get { return _name; }
            }
            public Criteria(int id)
            { _id = id; }

            public Criteria(string name)
            {
                _name = name;
            }
        }

        /// <summary>
        /// Override this method to load a new business object with default
        /// values from the database.
        /// </summary>
        /// <remarks>
        /// Normally you will overload this method to accept a strongly-typed
        /// criteria parameter, rather than overriding the method with a
        /// loosely-typed criteria parameter.
        /// </remarks>
        [RunLocal]
        protected override void DataPortal_Create()
        {
            //nothing to initialize
        }

        private void DataPortal_Fetch(Criteria criteria)
        {
            SelectSqlSection select =
                DataService.DBService.Select(Tables.Customers);
            NBearLite.WhereClip where = WhereClip.All;
            if (criteria.Id > 0)
            {
                where.And(Tables.Customers.Id == criteria.Id);
            }
            else
            {
                where.And(Tables.Customers.Name == criteria.Name);
            }
            select.Where(where);
            using (SafeDataReader dr = new SafeDataReader(select.ToDataReader()))
            {
                dr.Read();
                this._address = dr.GetString("Address");
                this._dealTimes = dr.GetInt32("DealTimes");
                this._fax = dr.GetString("Fax");
                this._id = dr.GetInt32("Id");
                this._name = dr.GetString("Name");
                this._tel = dr.GetString("Tel");
            }

            select = DataService.DBService.Select(Tables.Products)
                .Where(Tables.Products.CustomerId == _id);
            using (SafeDataReader dr = new SafeDataReader(select.ToDataReader()))
            {
                _products = Products.GetProducts(dr);
            }
        }

        [Transactional(TransactionalTypes.TransactionScope)]
        protected override void DataPortal_Insert()
        {
            InsertSqlSection insert =
                DataService.DBService.Insert(Tables.Customers)
                .AddColumn(Tables.Customers.Address, this._address)
                .AddColumn(Tables.Customers.DealTimes, this._dealTimes)
                .AddColumn(Tables.Customers.Fax, _fax)
                .AddColumn(Tables.Customers.Name, _name)
                .AddColumn(Tables.Customers.Tel, _tel);
            _id = insert.ExecuteReturnAutoIncrementID(Tables.Customers.Id);
        }

        [Transactional(TransactionalTypes.TransactionScope)]
        protected override void DataPortal_Update()
        {
            DataService.DBService.Update(Tables.Customers)
                .AddColumn(Tables.Customers.Address, this._address)
                .AddColumn(Tables.Customers.DealTimes, this._dealTimes)
                .AddColumn(Tables.Customers.Fax, _fax)
                .AddColumn(Tables.Customers.Name, _name)
                .AddColumn(Tables.Customers.Tel, _tel)
                .Where(Tables.Customers.Id == _id).Execute();

            _products.Update(this);
        }

        protected override void DataPortal_DeleteSelf()
        {
            DataPortal_Delete(new Criteria(_id));
        }

        [Transactional(TransactionalTypes.TransactionScope)]
        private void DataPortal_Delete(Criteria criteria)
        {
            DataService.DBService.Delete(Tables.Customers).Where(Tables.Customers.Id == criteria.Id).Execute();
        }

        #endregion

        #region CheckNameCommand

        [Serializable()]
        private class CheckNameCommand : CommandBase
        {
            #region Client-side Code

            string _name;
            bool _result = false;
            /// <summary>
            /// Gets a value indicating whether this <see cref="CheckNameCommand"/> is result.
            /// </summary>
            /// <value><c>true</c> if result; otherwise, <c>false</c>.</value>
            public bool Result
            {
                get { return _result; }
            }

            #endregion

            #region Factory Methods

            public CheckNameCommand(string name)
            {
                _name = name;
            }

            #endregion

            #region Server-side Code

            [Transactional(TransactionalTypes.TransactionScope)]
            protected override void DataPortal_Execute()
            {
                int c = (int)DB.DataService.DBService.Select(Tables.Customers, Tables.Customers.Id.Count())
                    .Where(Tables.Customers.Name == _name).ToScalar();
                _result = c != 0;
            }
            #endregion
        }

        #endregion
    }
}
